﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Activities.Statements;
using Microsoft.TeamFoundation.Build.Workflow.Activities;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.IO;
using System.ComponentModel;

namespace NDependActivities
{
    [BuildActivity(HostEnvironmentOption.Agent)]
    public class NDependActivity : Activity
    {
        // TODO: how to set up default values for arguments?
        [RequiredArgument]
        [Description("The server path to the solution to analayze")]
        public InArgument<String> ProjectToBuild { get; set; }

        [RequiredArgument]
        [Description("The path to the ndepend.console.exe on the Build Agent machine. You may use env variables.")]
        public InArgument<String> NDependConsolePath { get; set; }

        [RequiredArgument]
        [Description("The current build Workspace")]
        [DefaultValue("Workspace")]
        public InArgument<Workspace> Workspace { get; set; }

        [Description("Whether to store NDepend data into the build reports as build information nodes.")]
        [DefaultValue(true)]
        public InArgument<bool> LogNDependDataIntoTfs { get; set; }

        [RequiredArgument]
        [Description("The current build Workspace")]
        [DefaultValue("BinariesDirectory")]
        public InArgument<String> BinariesDirectory { get; set; }

        public NDependActivity()
        {
            Implementation = () => CreateBody();
        }

        private Activity CreateBody()
        {
            // solution path in the agent disk build tree
            var projectLocalPath = new Variable<string>();
            var ndependConsoleExpandedPath = new Variable<string>();
            var ndependResult = new Variable<int>();
            // Full path to the NDepend directory in the build folder
            var ndependReportDirectory = new Variable<String>();
            // Just the name of the NDepend directory subfolder (either found in the build tree or drop folder tree)
            var ndependReportDirectoryName = new Variable<String>();
            var ndependProjectPath = new Variable<String>();

            var stdOutput = new DelegateInArgument<string>();
            var stdError = new DelegateInArgument<string>();

            return new Sequence
            {
                Variables =
                {
                    projectLocalPath,
                    ndependConsoleExpandedPath,
                    ndependReportDirectory,
                    ndependReportDirectoryName,
                    ndependResult,
                    ndependProjectPath
                },
                Activities =
                {
                    new ConvertWorkspaceItem
                    {
                        DisplayName = "Convert project server path to local build path",
                        Direction = ConvertWorkspaceItemDirection.ServerToLocal,
                        Input = new InArgument<string>(env => ProjectToBuild.Get(env)),
                        Workspace = new InArgument<Workspace>(env => Workspace.Get(env)),
                        Result = projectLocalPath
                    },
                    new Assign<String>
                    {
                        DisplayName = "Set the NDepend project path",
                        To = ndependProjectPath,
                        Value = new InArgument<string>(env => projectLocalPath.Get(env).Replace(".sln", ".ndproj"))
                    },
                    new If
                    {
                        DisplayName = "If the NDepend project file exists",
                        Condition = new InArgument<bool>(env => File.Exists(ndependProjectPath.Get(env))),
                        Then = new Sequence
                        {
                            DisplayName = "Prepare NDepend execution",
                            Activities = 
                            {
                                new Assign<String>
                                {
                                    DisplayName = "Set the NDepend report directory name",
                                    To = ndependReportDirectoryName,
                                    Value = new InArgument<string>(env => Path.GetFileNameWithoutExtension(projectLocalPath.Get(env)) + "-NDepend")
                                },
                                new Assign<String>
                                {
                                    DisplayName = "Set the NDepend report directory path",
                                    To = ndependReportDirectory,
                                    Value = new InArgument<string>(env => Path.Combine(BinariesDirectory.Get(env), ndependReportDirectoryName.Get(env)))
                                },
                                new ExpandEnvironmentVariables
                                {
                                    DisplayName = "Expand environment variables from NDepend console path",
                                    Input = new InArgument<string>(env => NDependConsolePath.Get(env)),
                                    Result = ndependConsoleExpandedPath
                                },
                                new InvokeProcess
                                {
                                    DisplayName = "Invoke NDepend console executable",
                                    Arguments = new InArgument<String>(env => buildCommandLine(ndependProjectPath.Get(env), ndependReportDirectory.Get(env))),
                                    FileName = ndependConsoleExpandedPath,
                                    OutputDataReceived = new ActivityAction<string>
                                    {
                                        Argument = stdOutput,
                                        Handler = new WriteBuildMessage
                                        {
                                            Message = stdOutput
                                        }
                                    },
                                    ErrorDataReceived = new ActivityAction<string>
                                    {
                                        Argument = stdError,
                                        Handler = new WriteBuildWarning
                                        {
                                            Message = stdError
                                        }
                                    },
                                    Result = ndependResult
                                },
                                new If
                                {
                                    DisplayName = "If NDepend console reported no error",
                                    Condition = new InArgument<bool>(env => ndependResult.Get(env) == 0),
                                    Then = new WriteBuildMessage
                                    {
                                        Message = "NDepend rules successfully passed."
                                    },
                                    Else = new WriteBuildError
                                    {
                                        Message = new InArgument<String>(env => String.Format("Some NDepend critical rules are unsatisfied in project {0}. Please look at the NDepend report for more details.",
                                             Path.GetFileNameWithoutExtension(projectLocalPath.Get(env))))
                                    }
                                },
                                new If
                                {
                                    DisplayName = "If NDepend data must me logged into TFS",
                                    Condition = new InArgument<bool>(env => LogNDependDataIntoTfs.Get(env)),
                                    Then = new NDependDataLog
                                    {
                                        ReportPath = new InArgument<string>(env => Path.Combine(ndependReportDirectory.Get(env), "NDependMain.xml")),
                                        ReportLink = new InArgument<string>(env => buildReportLinkUrl(env, ndependReportDirectoryName.Get(env)))
                                    },
                                    Else = new WriteBuildMessage
                                    {
                                        Message = "Skipped logging NDepend data into Tfs"
                                    }
                                }

                            }
                        },
                        Else = new WriteBuildError
                        {
                            Message = new InArgument<string>(env => "NDepend project file " + ndependProjectPath.Get(env) + " not found.")
                        }
                    }
                    
                   
                }
            };
        }

        private string buildReportLinkUrl(ActivityContext context, string ndependReportDirectoryName)
        {
            IBuildDetail buildDetail = context.GetExtension<IBuildDetail>();
            return Path.Combine(buildDetail.DropLocation, ndependReportDirectoryName, "NDependReport.html");
        }

        private string buildCommandLine(string ndependProjectPath, string ndependReportDirectory)
        {           
            return String.Format("\"{0}\" /OutDir \"{1}\"",
                ndependProjectPath,
                ndependReportDirectory);

        }
    }
}
